package main

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"generalhandler/conf"
	"github.com/dgrijalva/jwt-go"
	"github.com/go-redis/redis"
	"github.com/kataras/iris"
	"github.com/lifei6671/gocaptcha"
	"github.com/satori/go.uuid"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type RedisHandler struct {
	redisClient *redis.Client
}

var instance *RedisHandler

func RedisHandlerInstance() *RedisHandler {
	return instance
}
func init() {
	fmt.Println("init in restapi ")
	instance = new(RedisHandler)
	instance.redisClient = redis.NewClient(&redis.Options{
		Addr:     "redis_1:6379",
		Password: "", //默认空密码
		DB:       0,  //使用默认数据库
	})
}

type Store struct {
	session *mgo.Session
}

var s *Store

func StoreInstance() *Store {
	if nil != s {
		return s
	}
	s = new(Store)
	err := s.Init()
	if nil != err {
		panic(err)
	}
	return s
}
func (s *Store) Init() error {
	var err error
	s.session, err = mgo.Dial(conf.StoreConfigInstance().StoreAddress)
	if nil != err {
		return fmt.Errorf("failed to mgo.Dial, cuz %s", err.Error())
	}
	s.session.SetMode(mgo.Monotonic, true)
	credential := &mgo.Credential{Username: conf.StoreConfigInstance().StoreUser,
		Password: conf.StoreConfigInstance().StorePwd}
	if err := s.session.Login(credential); err != nil {
		return fmt.Errorf("failed to mgo.Login, cuz %s", err.Error())
	}
	return nil
}
func (s *Store) Session() *mgo.Session {
	return s.session.Clone()
}

type Userinfo struct {
	Username     string
	Userpassword string
	Userid       string
}

var Verific map[string]string

type Messages struct {
	Ret  string
	Data string
}

func main() {
	conf.LoadStoreConf()
	err := gocaptcha.ReadFonts("./font", ".ttf")
	if err != nil {
		fmt.Println(err)
	}
	app := iris.Default()
	app.Get("/scan-verification", verification)
	app.Post("/scan-register", register)
	app.Post("/scan-login", scanLogin)
	app.Post("/scan-submit", scanSubmit)
	app.Get("/scan-task", scanTask)
	app.Get("/scan-result", scanResult)
	app.Get("/scan-result-stat", scanResultStat)
	app.Get("/scan-timing", crontiming)
	app.Post("/scan-delete", scandelete)
	app.Post("/scan-stop", scanstop)
	app.Post("/scan-resubmit", scanresubmit)
	app.Post("/scan-detail", scandetail)
	app.Post("/scan-timingsubmit", timingsubmit)
	app.Post("/time-timingstop", timingstop)

	app.Run(iris.Addr(":8088"))
}
func verification(ctx iris.Context) {
	image, _, uid := makeImageCode()
	data := Messages{
		Ret:  uid,
		Data: image,
	}
	str, err := json.Marshal(data)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.Writef(string(str))
}
func makeImageCode() (string, string, string) {
	captchaImage, err := gocaptcha.NewCaptchaImage(90, 30, gocaptcha.RandLightColor())
	captchaImage.DrawNoise(gocaptcha.CaptchaComplexLower)
	captchaImage.DrawTextNoise(gocaptcha.CaptchaComplexLower)
	imageCode := gocaptcha.RandText(4)
	id, _ := uuid.NewV4()
	Verific = make(map[string]string)
	Verific[id.String()] = imageCode
	captchaImage.DrawText(imageCode)
	captchaImage.DrawBorder(gocaptcha.ColorToRGB(0x17A7A7A))
	buf := &bytes.Buffer{}
	captchaImage.SaveImage(buf, gocaptcha.ImageFormatJpeg)
	if err != nil {
		fmt.Println(err)
	}
	sourcebuffer := make([]byte, 500000)
	n, _ := buf.Read(sourcebuffer)
	sourcestring := base64.StdEncoding.EncodeToString(sourcebuffer[:n])
	return sourcestring, imageCode, id.String()
}
func scandelete(ctx iris.Context) {
	token := ctx.FormValue("token")
	tkid := ctx.FormValue("tkid")
	if len(tkid) < 1 {
		returnErr(ctx, "Tkid is empty")
		return
	}
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	if len(userid) < 1 {
		returnErr(ctx, "Token error")
		return
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	tableresult := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanresult")
	tableparams := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanparams")
	err = table.Remove(bson.M{"tkid": tkid})
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			returnErr(ctx, "Delete failed")
			fmt.Println(err.Error())
			return
		}
	}
	err = tableresult.Remove(bson.M{"tkid": tkid})
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			returnErr(ctx, "Delete failed")
			fmt.Println(err.Error())
			return
		}
	}
	err = tableparams.Remove(bson.M{"tkid": tkid})
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			returnErr(ctx, "Delete failed")
			fmt.Println(err.Error())
			return
		}
	}
	returnRef(ctx, "true", "Delete successful")
	return
}
func scanstop(ctx iris.Context) {
	token := ctx.FormValue("token")
	tkid := ctx.FormValue("tkid")
	if len(tkid) < 1 {
		returnErr(ctx, "Tkid is empty")
		return
	}
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	if len(userid) < 1 {
		returnErr(ctx, "Token error")
		return
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	if err := table.Update(bson.M{"tkid": tkid}, bson.M{"$set": bson.M{"finished": 2}}); err != nil {
		returnErr(ctx, "failed to update"+tkid+err.Error())
		return
	}
	RedisHandlerInstance().redisClient.Set(tkid, 10000000, time.Hour*24*7) //停止标志 10000000
	returnRef(ctx, "true", tkid+"Task stop")
	return
}
func match(reg string) bool {
	match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9_]{6,32}$", reg)
	return match
}
func register(ctx iris.Context) {
	username := ctx.FormValue("username")
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("dbuser")
	var result Userinfo
	err := table.Find(&bson.M{"username": username}).Select(bson.M{"_id": 0}).One(&result)
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			returnErr(ctx, "Connection failed")
			fmt.Println(err.Error())
			return
		}
	}
	if len(result.Userid) > 1 {
		returnErr(ctx, "User name already exists")
		return
	}
	usererr := match(username)
	if 0 == len(username) && usererr != true {
		returnErr(ctx, "The user name can not be empty, the longest is 32 characters")
		return
	}
	password := ctx.FormValue("password")
	if 0 == len(password) {
		returnErr(ctx, "The password must not be empty")
		return
	}
	verifcode := ctx.FormValue("verifcode")
	uid := ctx.FormValue("uuid")
	if 0 == len(verifcode) {
		returnErr(ctx, "The verification code can not be empty")
		return
	}
	condtemp := Verific[uid]
	if len(condtemp) < 1 {
		returnErr(ctx, "Verification code expires")
		return
	}
	if strings.EqualFold(verifcode, condtemp) == false {
		Verific[uid] = " "
		returnErr(ctx, "Verification code error")
		return
	}
	Verific[uid] = " "
	userid, _ := uuid.NewV4()
	usertime := time.Now().Format("2006-01-02 15:04:05")
	err = table.Insert(bson.M{"username": username, "userpassword": password, "userid": userid.String(), "date": usertime})
	if err != nil {
		returnErr(ctx, "login has failed")
		return
	}
	returnRef(ctx, "true", "success")
	return
}
func GenerateToken(userid string) (string, error) {
	timeStr := time.Now().Format("2006-01-02")
	t, _ := time.ParseInLocation("2006-01-02 15:04:05", timeStr+" 23:59:59", time.Local)
	hh, _ := time.ParseDuration("2h")
	Tomorrowtime := t.Add(hh).Unix()
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"userid": userid,
		"exp":    Tomorrowtime, // 可以添加过期时间
	})

	return token.SignedString([]byte("tokenuserinfo")) //对应的字符串请自行生成，最后足够使用加密后的字符串
}
func ParseToken(tokenStr string) (string, error) {
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("not Token or Token error")
		}
		return []byte("tokenuserinfo"), nil
	})
	if nil != err {
		return "", err
	}
	if token.Valid == false {
		return "false", fmt.Errorf("token Be overdue")
	}
	userid := token.Claims.(jwt.MapClaims)["userid"]
	var result Userinfo
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("dbuser")
	usererr := table.Find(&bson.M{"userid": userid}).One(&result)
	if nil != usererr {
		if usererr.Error() != mgo.ErrNotFound.Error() {
			panic(usererr.Error())
		} else {
			return "false", fmt.Errorf("user does not exist")
		}
	}
	return result.Userid, nil
}
func scanLogin(ctx iris.Context) {
	username := ctx.FormValue("username")
	password := ctx.FormValue("password")
	if 0 == len(username) || 0 == len(password) {
		returnErr(ctx, "need username or password")
		return
	}

	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("dbuser")
	var result Userinfo
	err := table.Find(&bson.M{"username": username, "userpassword": password}).Select(bson.M{"_id": 0}).One(&result)
	if nil != err {
		if err.Error() != mgo.ErrNotFound.Error() {
			panic(err.Error())
		} else {
			fmt.Println(err.Error())
			returnErr(ctx, "Incorrect user name or password")
		}
		return
	} else {
		if result.Username != username {
			returnErr(ctx, "Incorrect user name or password")
			return
		}
	}
	token, err := GenerateToken(string(result.Userid))
	if err != nil {
		returnErr(ctx, "token Exchange for fail")
		fmt.Println(err.Error())
		return
	}
	returnRef(ctx, "true", token)
	return
}
func scanSubmit(ctx iris.Context) {
	domain := ctx.FormValue("domain")
	subdomain := ctx.FormValue("subdomain")
	scanName := ctx.FormValue("scan-name")
	company := ctx.FormValue("company")
	ignore := ctx.FormValue("ignore")
	execdate := ctx.FormValue("execdate")
	useragent := ctx.FormValue("useragent")
	file, _, err := ctx.FormFile("uploadfile")
	var uploadfileFlag = 1
	if err != nil {
		uploadfileFlag = 0
	}
	if (0 == len(domain) && 0 == len(subdomain) && err != nil) || 0 == len(scanName) {
		if err != nil {
			ctx.Writef(err.Error())
		} else {
			ctx.Writef("need domain or scan-name")
		}
		ctx.StatusCode(400)
		return
	}
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	if 0 == len(useragent) {
		useragent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36"
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	tkID, _ := uuid.NewV4()
	var resp *http.Response
	var subdomains []string
	if uploadfileFlag == 1 {
		br := bufio.NewReader(file)
		defer file.Close()
		for {
			subdomain, _, c := br.ReadLine()
			subdomainStr := strings.TrimSpace(string(subdomain))
			if c == io.EOF {
				break
			} else if len(subdomainStr) == 0 {
				continue
			}
			subdomains = append(subdomains, subdomainStr)
			if strings.Contains(ignore, "pos-nmap-whole") {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomainStr, subdomainStr, tkID.String(), company, userid, ignore, useragent)))
			} else {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap-whole",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomainStr, subdomainStr, tkID.String(), company, userid, ignore, useragent)))
			}
		}
	} else if len(subdomain) > 0 {
		if strings.Contains(ignore, "pos-nmap-whole") {
			resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap",
				"json",
				strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomain, subdomain, tkID.String(), company, userid, ignore, useragent)))
		} else {
			resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap-whole",
				"json",
				strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomain, subdomain, tkID.String(), company, userid, ignore, useragent)))
		}
	} else {
		resp, err = http.Post("http://nsqd_1:4151/pub?topic=scanlineep",
			"json",
			strings.NewReader(fmt.Sprintf(`{"domain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, domain, tkID.String(), company, userid, ignore, useragent)))
	}
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		panic(err)
	}
	if 0 != strings.Compare(string(body), "OK") {
		panic(string(body))
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	tables := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanparams")
	if len(subdomains) > 0 {
		scanstring := strings.Replace(strings.Trim(fmt.Sprint(subdomains), "[]"), " ", ",", -1)
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore, "subdomains": scanstring}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"execdate": execdate, "company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "batch", "useragent": useragent})
	} else if len(subdomain) > 0 {
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"execdate": execdate, "subdomain": subdomain, "company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "subdomain", "useragent": useragent})
	} else if len(domain) > 0 {
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"execdate": execdate, "domain": domain, "company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "domain", "useragent": useragent})
	}
	if nil != err {
		panic(err)
	}
	var redisCount = 1
	if len(subdomains) > 0 {
		redisCount = len(subdomains)
	}
	RedisHandlerInstance().redisClient.Set(tkID.String(), redisCount, time.Hour*24*7)
	returnRef(ctx, "true", "success")
	return
}
func getValue(m bson.M, key string) string {
	value := m[key]
	if value == nil {
		return ""
	} else {
		return value.(string)
	}
}
func scanresubmit(ctx iris.Context) {
	tkid := ctx.FormValue("tkid")
	var result []bson.M
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	err := table.Find(&bson.M{"tkid": tkid}).Limit(1).All(&result)
	if err != nil {
		panic(err)
	}
	var cursor uint64
	var val []string
	for {
		var keys []string
		var err error
		keys, cursor, err = RedisHandlerInstance().redisClient.Scan(cursor, tkid+"*", 0).Result()
		if err != nil {
			fmt.Println("failed to scanredis , cuz %s,%s", tkid, err.Error())
			continue
		}
		val = append(val, keys...)
		if cursor == 0 {
			break
		}
	}
	for k, _ := range val {
		RedisHandlerInstance().redisClient.Del(val[k])
	}
	var m bson.M
	if len(result) > 0 {
		m = result[0]
	} else {
		returnErr(ctx, "Cannot find task record")
		return
	}
	domain := getValue(m, "domain")
	domainType := getValue(m, "domaintype")
	scanName := getValue(m, "name")
	company := getValue(m, "company")
	subdomain := getValue(m, "subdomain")
	useragent := getValue(m, "useragent")
	var scanParamsResult []bson.M
	tableParam := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanparams")
	err = tableParam.Find(&bson.M{"tkid": tkid}).Limit(1).All(&scanParamsResult)
	if nil != err {
		panic(err)
	}
	if len(scanParamsResult) > 0 {
		m = scanParamsResult[0]
	}
	var uploadfileFlag = 0
	uploadfile := getValue(m, "subdomains")
	ignore := getValue(m, "ignore")
	if domainType == "batch" && len(uploadfile) > 0 {
		uploadfileFlag = 1
	}
	if (0 == len(domain) && 0 == len(subdomain) && 0 == uploadfileFlag) || 0 == len(scanName) {
		returnErr(ctx, "need domain or scan-name"+err.Error())
		return
	}
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	tkID, _ := uuid.NewV4()
	var resp *http.Response
	var subdomains []string
	if uploadfileFlag == 1 {
		brs := strings.Split(uploadfile, ",")
		for _, e := range brs {
			subdomainStr := strings.TrimSpace(string(e))
			if len(subdomainStr) == 0 {
				continue
			}
			subdomains = append(subdomains, subdomainStr)
			if strings.Contains(ignore, "pos-nmap-whole") {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomainStr, subdomainStr, tkID.String(), company, userid, ignore, useragent)))
			} else {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap-whole",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomainStr, subdomainStr, tkID.String(), company, userid, ignore, useragent)))
			}
		}
	} else if len(subdomain) > 0 {
		if strings.Contains(ignore, "pos-nmap-whole") {
			resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap",
				"json",
				strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomain, subdomain, tkID.String(), company, userid, ignore, useragent)))
		} else {
			resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap-whole",
				"json",
				strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, subdomain, subdomain, tkID.String(), company, userid, ignore, useragent)))
		}
	} else {
		resp, err = http.Post("http://nsqd_1:4151/pub?topic=scanlineep",
			"json",
			strings.NewReader(fmt.Sprintf(`{"domain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, domain, tkID.String(), company, userid, ignore, useragent)))
	}
	if nil != err {
		panic(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		panic(err)
	}
	if 0 != strings.Compare(string(body), "OK") {
		panic(string(body))
	}
	tables := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanparams")
	if len(subdomains) > 0 {
		scanstring := strings.Replace(strings.Trim(fmt.Sprint(subdomains), "[]"), " ", ",", -1)
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore, "subdomains": scanstring}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "batch", "useragent": useragent})
	} else if len(subdomain) > 0 {
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"subdomain": subdomain, "company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "subdomain", "useragent": useragent})
	} else if len(domain) > 0 {
		if err = tables.Insert(bson.M{"tkid": tkID.String(), "ignore": ignore}); err != nil {
			panic(err)
		}
		err = table.Insert(bson.M{"domain": domain, "company": company, "finished": 0, "name": scanName, "tkid": tkID.String(), "create": time.Now().Unix(), "userid": userid, "domaintype": "domain", "useragent": useragent})
	}
	if nil != err {
		panic(err)
	}
	var redisCount = 1
	if len(subdomains) > 0 {
		redisCount = len(subdomains)
	}
	err = table.Remove(bson.M{"tkid": tkid})
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			fmt.Println(err.Error())
			returnErr(ctx, "Delete failed")
			return
		}
	}
	tableresult := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scanresult")
	_, err = tableresult.RemoveAll(bson.M{"tkid": tkid})
	if err != nil {
		if err.Error() != mgo.ErrNotFound.Error() {
			returnErr(ctx, "Delete failed")
			fmt.Println(err.Error())
			return
		}
	}
	RedisHandlerInstance().redisClient.Set(tkID.String(), redisCount, time.Hour*24*7)
	returnRef(ctx, "true", "success")
	return
}
func scanTask(ctx iris.Context) {
	skip, err := ctx.URLParamInt("skip")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	limit, err := ctx.URLParamInt("limit")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}

	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	var result []bson.M
	err = table.Find(&bson.M{"userid": userid}).Skip(skip).Limit(limit).Sort("-create").Select(bson.M{"_id": 0}).All(&result)
	if nil != err {
		panic(err)
	}
	r, err := json.Marshal(result)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.WriteString(string(r))
}
func scanResult(ctx iris.Context) {
	tkID := ctx.URLParam("tk-id")
	scanType := ctx.URLParam("scan-type")
	subdomain := ctx.URLParam("subdomain")
	port := ctx.URLParam("port")
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	if 0 == len(tkID) && 0 == len(scanType) {
		returnErr(ctx, "need tk-id or scan-type")
		return
	}
	skip, err := ctx.URLParamInt("skip")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	limit, err := ctx.URLParamInt("limit")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C(conf.StoreConfigInstance().StoreTable)
	var result []bson.M
	selector := map[string]interface{}{"tkid": tkID, "scan_result.scan_type": scanType}
	if 0 != len(subdomain) {
		selector["subdomain"] = subdomain
	}
	if 0 != len(port) {
		selector["port"] = port
	}
	if 0 != len(token) {
		selector["userid"] = userid
	}
	err = table.Find(bson.M(selector)).Skip(skip).Limit(limit).Select(bson.M{"_id": 0}).All(&result)
	if nil != err {
		panic(err)
	}
	r, err := json.Marshal(result)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.WriteString(string(r))
}
func scanResultStat(ctx iris.Context) {
	skip, err := ctx.URLParamInt("skip")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	limit, err := ctx.URLParamInt("limit")
	if nil != err {
		returnErr(ctx, err.Error())
		return
	}
	tkID := ctx.URLParam("tk-id")
	scanType := ctx.URLParam("scan-type")
	if 0 == len(tkID) {
		returnErr(ctx, "need tk-id")
		return
	}
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C(conf.StoreConfigInstance().StoreTable)
	var pipe []bson.M
	if 0 != len(scanType) {
		pipe = append(pipe, bson.M{"$match": bson.M{"tkid": tkID, "scan_result.scan_type": scanType}})
		pipe = append(pipe, bson.M{"$match": bson.M{"userid": userid}})
		pipe = append(pipe, bson.M{"$project": bson.M{"_id": 0}})
		pipe = append(pipe, bson.M{"$group": bson.M{"_id": bson.M{"prefix": "$prefix", "subdomain": "$subdomain", "port": "$port"}}})
	} else {
		pipe = append(pipe, bson.M{"$match": bson.M{"tkid": tkID}})
		pipe = append(pipe, bson.M{"$match": bson.M{"userid": userid}})
		pipe = append(pipe, bson.M{"$project": bson.M{"scan_result.scan_type": 1, "_id": 0}})
		pipe = append(pipe, bson.M{"$group": bson.M{"_id": "$scan_result.scan_type", "count": bson.M{"$sum": 1}}})
	}
	pipe = append(pipe, bson.M{"$skip": skip})
	pipe = append(pipe, bson.M{"$limit": limit})
	var result []bson.M
	err = table.Pipe(pipe).Iter().All(&result)
	if nil != err {
		panic(err)
	}
	r, err := json.Marshal(result)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.WriteString(string(r))
}
func scandetail(ctx iris.Context) {
	token := ctx.FormValue("token")
	tkid := ctx.FormValue("tkid")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantask")
	var result bson.M
	err = table.Find(&bson.M{"userid": userid, "tkid": tkid}).Select(bson.M{"_id": 0, "scanredis": 1, "finished": 1}).One(&result)
	var arr []interface{}
	if result["scanredis"] == nil {
		var cursor uint64
		var val []string
		for {
			var keys []string
			var err error
			keys, cursor, err = RedisHandlerInstance().redisClient.Scan(cursor, tkid+"*", 0).Result()
			fmt.Println(val)
			if err != nil {
				fmt.Println("failed to scanredis , cuz %s,%s", tkid, err.Error())
				continue
			}
			val = append(val, keys...)
			if cursor == 0 {
				break
			}
		}
		for k, _ := range val {
			value, _ := RedisHandlerInstance().redisClient.Get(val[k]).Result()
			if sum := strings.Index(value, "{"); sum != -1 {
				var jsonValue map[string]interface{}
				if err := json.Unmarshal([]byte(value), &jsonValue); err == nil {
					zhi := strings.Split(val[k], ":")
					if len(zhi) > 1 {
						jsonValue["type"] = zhi[1]
					}
					arr = append(arr, jsonValue)
				} else {
					panic("Conversion error")
				}
			}
		}
	} else {
		arr = append(arr, result)
	}
	if nil != err {
		panic(err)
	}
	d, err := json.Marshal(arr)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.WriteString(string(d))
}
func timingstop(ctx iris.Context) {
	tkid := ctx.FormValue("timingtkid")
	token := ctx.FormValue("token")
	if len(token) < 1 {
		returnErr(ctx, "Token is empty")
		return
	}
	userid, err := ParseToken(string(token))
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantiming")
	if err := table.Update(bson.M{"userid": userid, "timingid": tkid}, bson.M{"$set": bson.M{"timefinished": 1}}); err != nil {
		returnErr(ctx, "failed to update, cuz %s %s"+tkid+err.Error())
		return
	}
	userFile := "./timing/timing.cron"
	buf, err := ioutil.ReadFile(userFile) //将整个文件的内容读到一个字节切片中。
	if err != nil {
		fmt.Fprintf(os.Stderr, "File Error: %s\n", err)
	}
	str3 := strings.Split(string(buf), "\n")
	f, _ := os.OpenFile(userFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
	defer f.Close()
	for _, v := range str3 {
		if strings.Index(string(v), tkid) >= 0 || len(v) == 0 {
			continue
		} else {
			f.WriteString(string(v + "\n"))
		}
	}
	cmd := exec.Command("crontab", "./timing/timing.cron")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}
	defer stdout.Close()
	cmd.Run()
	returnRef(ctx, "true", "Stop execution--"+tkid)
	return
}
func timingsubmit(ctx iris.Context) {
	domain := ctx.FormValue("domain")
	useragent := ctx.FormValue("useragent")
	if 0 == len(useragent) {
		useragent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36"
	}
	domaintype := ctx.FormValue("domaintype") //Subdomain Domain batch
	if domaintype != "batch" {
		if len(domain) < 1 {
			returnErr(ctx, "Domain name cannot be empty")
			return
		}
	}
	name := ctx.FormValue("scan-name")
	company := ctx.FormValue("company")
	ignore := ctx.FormValue("ignore")
	timedate := ctx.FormValue("time")
	if len(timedate) < 1 {
		returnErr(ctx, "Timed task execution time can not be empty")
		return
	}
	timingtkid, _ := uuid.NewV4()
	//创建定时任务时间
	timeUnix := time.Now().Unix()
	token := ctx.FormValue("token")
	userid, err := ParseToken(string(token))
	if err != nil {
		returnRef(ctx, "false", "token Incorrect")
		return
	}
	var subdomains []string
	table := StoreInstance().Session().DB(conf.StoreConfigInstance().StoreDB).C("scantiming")
	userFile := "./timing/timing.cron"
	f, _ := os.OpenFile(userFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
	defer f.Close()
	var strtime string
	var strcmd string
	if domaintype != "batch" {
		err = table.Insert(bson.M{"company": company, "timingid": timingtkid.String(), "name": name, "domaintype": domaintype, "domain": domain, "timefinished": 0, "execdate": timedate, "userid": userid, "create": timeUnix, "ignore": ignore, "useragent": useragent})
	} else {
		file, _, err := ctx.FormFile("uploadfile")
		if err != nil {
			returnErr(ctx, "Please upload the file")
			return
		}
		br := bufio.NewReader(file)
		for {
			subdomain, _, c := br.ReadLine()
			subdomainStr := strings.TrimSpace(string(subdomain)) //每一个域名
			if c == io.EOF {
				break
			} else if len(subdomainStr) == 0 {
				continue
			}
			subdomains = append(subdomains, subdomainStr)
		}
		scanstring := strings.Replace(strings.Trim(fmt.Sprint(subdomains), "[]"), " ", ",", -1)
		err = table.Insert(bson.M{"company": company, "timingid": timingtkid.String(), "name": name, "domaintype": domaintype, "domain": scanstring, "timefinished": 0, "execdate": timedate, "userid": userid, "create": timeUnix, "ignore": ignore, "useragent": useragent})
	}
	if err != nil {
		panic(err)
	}
	timestr := strings.Split(timedate, ":")
	if len(timestr) == 2 {
		if crontime(timestr) {
			strtime = string(timestr[1]) + " " + string(timestr[0]) + " * * * "
		}
	} else if len(timestr) == 3 {
		if crontime(timestr) {
			strtime = string(timestr[1]) + " " + string(timestr[0]) + " * * " + string(timestr[2]) + " "
		}
	} else {
		returnErr(ctx, "Please enter the correct time format")
		return
	}
	strcmd = "curl -v restapi_1:8088/scan-timing?tkid=" + timingtkid.String() + " &> timing.log"
	f.WriteString(strtime + strcmd + "\n")
	cmd := exec.Command("crontab", "./timing/timing.cron")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}
	// 保证关闭输出流
	defer stdout.Close()
	// 运行命令
	err = cmd.Run()
	if err != nil {
		panic(err)
	}
	returnRef(ctx, "true", "Start execution--"+timingtkid.String())
	return
}
func crontime(timestr []string) bool {
	if len(timestr) == 2 {
		M, _ := strconv.Atoi(timestr[1])
		H, _ := strconv.Atoi(timestr[0])
		if H >= 0 && H < 24 && M >= 0 && M <= 59 {
			return true
		} else {
			return false
		}
	} else if len(timestr) == 3 {
		M, _ := strconv.Atoi(timestr[1])
		H, _ := strconv.Atoi(timestr[0])
		W, _ := strconv.Atoi(timestr[2])
		if H >= 0 && H < 24 && M >= 0 && M < 59 && W >= 0 && W <= 7 {
			return true
		} else {
			return false
		}
	}
	return true
}
func crontiming(ctx iris.Context) {
	crontkid := ctx.FormValue("tkid")
	if len(crontkid) < 1 {
		returnErr(ctx, "Tkid is empty")
		return
	}
	table := StoreInstance().Session().DB("scanline").C("scantiming")
	tables := StoreInstance().Session().DB("scanline").C("scantask")
	var timedata bson.M
	err := table.Find(bson.M{"timingid": crontkid}).One(&timedata)
	if err != nil {
		returnErr(ctx, "Please enter the correct tkid")
		return
	}
	timetkid, _ := uuid.NewV4()
	var resp *http.Response
	var subdomains []string
	token, err := GenerateToken(timedata["userid"].(string))
	if timedata["domaintype"] == "Domain" {
		resp, err = http.PostForm("http://restapi_1:8088/scan-submit",
			url.Values{"execdate": {timedata["execdate"].(string)}, "useragent": {timedata["useragent"].(string)}, "token": {token}, "domain": {timedata["domain"].(string)}, "scan-name": {timedata["name"].(string)}, "company": {timedata["company"].(string)}, "ignore": {timedata["ignore"].(string)}})
	} else if timedata["domaintype"] == "Subdomain" {
		resp, err = http.PostForm("http://restapi_1:8088/scan-submit",
			url.Values{"execdate": {timedata["execdate"].(string)}, "useragent": {timedata["useragent"].(string)}, "token": {token}, "subdomain": {timedata["domain"].(string)}, "domain": {timedata["domain"].(string)}, "scan-name": {timedata["name"].(string)}, "company": {timedata["company"].(string)}, "ignore": {timedata["ignore"].(string)}})
	} else if timedata["domaintype"] == "timebatchdomain" {
		err = tables.Insert(bson.M{"execdate": timedata["execdate"], "timingid": crontkid, "company": timedata["company"], "finished": 0, "name": timedata["name"], "tkid": timetkid.String(), "create": time.Now().Unix(), "userid": timedata["userid"], "domaintype": "batch"})
		timearr := strings.Split(timedata["domain"].(string), ",")
		for _, v := range timearr {
			subdomains = append(subdomains, v)
			if strings.Contains(timedata["ignore"].(string), "pos-nmap-whole") {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, v, v, timetkid.String(), timedata["company"], timedata["userid"], timedata["ignore"], timedata["useragent"])))
			} else {
				resp, err = http.Post("http://nsqd_1:4151/pub?topic=pos-nmap-whole",
					"json",
					strings.NewReader(fmt.Sprintf(`{"domain":"%s","subdomain":"%s","tkid":"%s","company":"%s","userid":"%s","ignore":"%s","useragent":"%s"}`, v, v, timetkid.String(), timedata["company"], timedata["userid"], timedata["ignore"], timedata["useragent"])))
			}
		}
	} else {
		returnErr(ctx, "Delivery failure")
		return
	}
	var redisCount = 1
	if len(subdomains) > 0 {
		redisCount = len(subdomains)
	}
	RedisHandlerInstance().redisClient.Set(timetkid.String(), redisCount, time.Hour*24*7)
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
}
func returnErr(ctx iris.Context, str string) {
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.StatusCode(400)
	ctx.Writef(str)
	return
}
func returnRef(ctx iris.Context, ret string, dat string) {
	data := Messages{
		Ret:  ret,
		Data: dat,
	}
	str, err := json.Marshal(data)
	if nil != err {
		panic(err)
	}
	ctx.Header("Access-Control-Allow-Origin", "*")
	ctx.Writef(string(str))
	return
}
